CodePipelineのS3へのデプロイを使って、CloudFormation一撃で静的Webサイトの構築をやってみる
福岡オフィスの梶原です。先日、CodePipelineでS3へのデプロイがサポートされました。
AWS CodePipeline で Amazon S3 へのデプロイのサポートを開始 https://aws.amazon.com/jp/about-aws/whats-new/2019/01/aws-codepipeline-now-supports-deploying-to-amazon-s3/
ということで、そのままお知らせするのもなんなので勢いあまって
GithubリポジトリにあるWebコンテンツをCodePipelineでS3にデプロイ展開し、CloudFrontで配信するAWS環境を一撃で作るCloudFormation Templateを作ったので公開します (なげーよ
※注意(2019/02/25 追記) S3へのデプロイですが、現時点ではgithub側のコンテンツ削除が同期されません。本番使用する場合はデプロイ前に削除 または 別途syncを実施するなどの対応が必要です。
構成図
いるもの
- AWSアカウント
- Githubアカウント
- Webコンテンツ(とりあえず、index.html)
Github側の準備
リポジトリとコンテンツの準備
Githubにprivateのリポジトリを作成して、直下にwebコンテンツを置きます。 ここでは、単純にindex.htmlを置きます。
OAuthトークンの作成と取得
下記URLにアクセスしてGithubのOAuthのトークンを取得します
https://github.com/settings/tokens
[generate new token]
を押下します token descriptionはわかりやすい名前をつけてください□repo
,□admin:repo_hook
をチェックします。[generato token]
を押下します。
tokenが生成されるのでメモしておきます
CloudFormationで一撃
使用するCloudFormationのテンプレートはこちらになります。
AWSTemplateFormatVersion: '2010-09-09' Metadata: AWS::CloudFormation::Interface: ParameterGroups: - Label: default: "Github Repository Settings" Parameters: - GitHubOwner - GitHubRepo - GitHubBranch - Label: default: "Github OAuth" Parameters: - GitHubOAuthToken ParameterLabels: GitHubOwner: default: "Owner" GitHubRepo: default: "Repository" GitHubBranch: default: "Branch" GitHubOAuthToken: default: "OAuthToken" Parameters: GitHubOwner: Type: String GitHubRepo: Type: String GitHubBranch: Type: String Default: master GitHubOAuthToken: Type: String NoEcho: true Resources: # S3 bucket contains static contents S3Bucket: Type: AWS::S3::Bucket DeletionPolicy: Retain CloudFrontOriginAccessIdentity: Type: AWS::CloudFront::CloudFrontOriginAccessIdentity Properties: CloudFrontOriginAccessIdentityConfig: Comment: !Ref AWS::StackName # S3 bucket policy to allow access from CloudFront OAI S3BucketPolicy: Type: AWS::S3::BucketPolicy Properties: Bucket: !Ref S3Bucket PolicyDocument: Statement: - Sid: "1" Action: s3:GetObject Effect: Allow Resource: !Sub arn:aws:s3:::${S3Bucket}/* Principal: AWS: !Sub arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity ${CloudFrontOriginAccessIdentity} # CloudFront Distribution for contents delivery S3Distribution: Type: AWS::CloudFront::Distribution Properties: DistributionConfig: Enabled: true Origins: - Id: !Sub S3-${S3Bucket.DomainName} DomainName: !GetAtt S3Bucket.DomainName S3OriginConfig: OriginAccessIdentity: !Sub origin-access-identity/cloudfront/${CloudFrontOriginAccessIdentity} DefaultRootObject: index.html DefaultCacheBehavior: TargetOriginId: !Sub S3-${S3Bucket.DomainName} ViewerProtocolPolicy: allow-all ForwardedValues: QueryString: false PriceClass: PriceClass_200 CodePipelineArtifactStoreBucket: Type: AWS::S3::Bucket DeletionPolicy: Retain CodePipelineServiceRole: Type: AWS::IAM::Role Properties: Path: /service-role/ AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: codepipeline.amazonaws.com Action: sts:AssumeRole Policies: - PolicyName: root PolicyDocument: Version: '2012-10-17' Statement: - Action: - iam:PassRole Resource: '*' Effect: Allow Condition: StringEqualsIfExists: iam:PassedToService: - cloudformation.amazonaws.com - Action: - codedeploy:CreateDeployment - codedeploy:GetApplication - codedeploy:GetApplicationRevision - codedeploy:GetDeployment - codedeploy:GetDeploymentConfig - codedeploy:RegisterApplicationRevision Resource: '*' Effect: Allow - Action: - cloudwatch:* - s3:* - cloudformation:* Resource: '*' Effect: Allow Pipeline: Type: AWS::CodePipeline::Pipeline Properties: ArtifactStore: Type: S3 Location: !Ref CodePipelineArtifactStoreBucket RoleArn: !GetAtt CodePipelineServiceRole.Arn Stages: - Name: Source Actions: - Name: SourceAction ActionTypeId: Category: Source Owner: ThirdParty Version: 1 Provider: GitHub Configuration: Owner: !Ref GitHubOwner Repo: !Ref GitHubRepo Branch: !Ref GitHubBranch OAuthToken: !Ref GitHubOAuthToken PollForSourceChanges: false OutputArtifacts: - Name: SourceArtifact RunOrder: 1 - Name: Deploy Actions: - Name: DeployAction ActionTypeId: Category: Deploy Owner: AWS Provider: S3 Version: 1 InputArtifacts: - Name: SourceArtifact Configuration: BucketName: !Ref S3Bucket Extract: true # OutputArtifacts: [] RunOrder: 1 GitHubSecret: Type: AWS::SecretsManager::Secret Properties: GenerateSecretString: SecretStringTemplate: '{}' GenerateStringKey: "SecretToken" ExcludePunctuation: true PasswordLength: 40 PipelineWebhook: Type: AWS::CodePipeline::Webhook Properties: Authentication: GITHUB_HMAC AuthenticationConfiguration: SecretToken: !Join ['', ['{{resolve:secretsmanager:', !Ref GitHubSecret, ':SecretString:SecretToken}}' ]] Filters: - JsonPath: "$.ref" MatchEquals: refs/heads/{Branch} TargetPipeline: !Ref Pipeline TargetAction: SourceAction TargetPipelineVersion: !GetAtt Pipeline.Version RegisterWithThirdParty: true Outputs: S3OriginBucket: Value: !Join [ "", [ "https://s3.console.aws.amazon.com/s3/buckets/", !Ref S3Bucket ]] CloudFrontUrl: Value: !Join [ "", [ "https://", !GetAtt [ S3Distribution, DomainName ]]]
ファイルはS3に置いたので、AWSアカウントにログイン後下記URLをクリックしてみるとCloudFormationのコンソールが立ち上がりますので
配布元のGithubリポジトリなどを変更して、先ほど取得したOAuthトークンを設定してください。
CloudFormationのポイント
CodePipeline のS3展開部分
- Name: Deploy Actions: - Name: DeployAction ActionTypeId: Category: Deploy Owner: AWS Provider: S3 Version: 1 InputArtifacts: - Name: SourceArtifact Configuration: BucketName: !Ref S3Bucket Extract: true # OutputArtifacts: [] RunOrder: 1
S3 のデプロイ部分は上記記載の部分になります。
S3のOAIを使用してCloudFrontコンテンツを配信するので、Extract:true
を指定してオリジンのS3バケットにファイルを展開しています。
GithubのWebhook
GitHubSecret: Type: "AWS::SecretsManager::Secret" Properties: GenerateSecretString: SecretStringTemplate: '{}' GenerateStringKey: "SecretToken" ExcludePunctuation: true PasswordLength: 40 PipelineWebhook: Type: AWS::CodePipeline::Webhook Properties: Authentication: GITHUB_HMAC AuthenticationConfiguration: SecretToken: !Join ['', ['{{resolve:secretsmanager:', !Ref GitHubSecret, ':SecretString:SecretToken}}' ]] Filters: - JsonPath: "$.ref" MatchEquals: refs/heads/{Branch} TargetPipeline: !Ref Pipeline TargetAction: SourceAction TargetPipelineVersion: !GetAtt Pipeline.Version RegisterWithThirdParty: true
GithubのOAuthの権限でWebHookを作成できるようにしてますので(repo_hookのチェックの部分) CodepipeLineのGithub WebHook連携機能を使って、GithubのWebHookを作成しCommitされた場合に自動でCodePipelineのデプロイが動くようにしています。 また、WebHookを作成する際は、CloudFormationでSecretsManagerにランダムなTokenを生成して動的な参照(Dynamic Reference)してWebHookのパラメータに使用しています。
Github のリポジトリページの[Settings][WebHook]
にWebHookが作成されていますので確認してみてください。
また、生成したトークンの値はSecretsManagerでも確認できます。
アクセスしてみる
CloudFormationのOutputにCloudFrontのURLが出力されていますアクセスしてみてください。
※注 CloudFormationの作成が終わってもCloudFrontの展開状況によっては以下のアクセス拒否の下記エラーがでる可能性があります。解消まである程度時間がかかるのでお待ちください。
This XML file does not appear to have any style information associated with it. The document tree is shown below.
コンテンツを更新してみる
githubのリポジトリのコンテンツを更新また、作成してみてください。 CodePipelineのデプロイが走り、S3のコンテンツが更新されます。 再度CloudFrontのURLにアクセスしコンテンツが更新されていれば成功です。 (キャッシュが効いていることもあるのでスーパーリロードなど実施してください)
まとめ
S3のデプロイがサポートされる前は、CodeBuild等を使用してS3へsyncで展開などを行っていましたが、CodePipelineでサポートされたので手軽に静的コンテンツのデプロイが実施できるようになりました。 S3でWeb配信以外にも、単純なgithubのリポジトリのバックアップなどにも使えそうな気がしています。 (容量が大きくなるとS3に展開されるファイルも増えますので実際に使用する際は一時バケット(ArtifactStoreBucket)のLifeCycleなどの設定をお願いします)
おそらく、このテンプレートをそのまま使う事はあまりなく、独自ドメインが使いたいとかはきっとあると思うので、いい感じにカスタマイズして使用してください。 誰かの役に立つと嬉しいです。もっともっとカスタマイズしたい、もう無理ぽ。とかなれば
クラスメソッドの「プレミアムオプション」などもご検討ください。
https://classmethod.jp/services/members/
参考
https://docs.aws.amazon.com/ja_jp/codepipeline/latest/userguide/tutorials-s3deploy.html
https://docs.aws.amazon.com/codepipeline/latest/userguide/pipelines-webhooks-create-cfn.html